#!/usr/bin/python
# -*- coding:UTF-8 -*-

import os
import argparse
import sys
import json

binPaths = os.path.split(os.path.realpath(__file__))
dictPath = os.path.realpath(binPaths[0])
homePath = os.path.realpath(binPaths[0]+'/../..')
sys.path.append(homePath+'/lib')
sys.path.append(homePath+'/plugins/local/lib')


def usage():
    pname = os.path.basename(__file__)
    print(pname + " --lang <lang> eg : zh_CN or en_US ,default value zh_CN.\n")
    print("         --mode <mode> eg : truncate or append ,default value truncate.\n")
    print(" --action [beautify|remove|append|savetodb|dropcollection｜cleancollection] \n")
    print("Examples:")
    print("Remove field:")
    print('dicttool --lang en_US --action remove --filter \'{"collection": "COLLECT_INS"}\' --content BELONG_APPLICATION')
    exit(1)


def getDefaultFieldKeys():
    defaultKeys = [
        '_OBJ_CATEGORY',
        '_OBJ_TYPE',
        'WAS_TYPE',
        'HOSTNAME',
        'NAME',
        'SERVER_NAME',
        'INSTANCE_NAME',
        'UNIQUE_NAME',
        'PRODUCT_NAME',
        'MGMT_IP',
        'MGMT_PORT',
        'MAJOR_VERSION',
        'VERSION',
        'KERNEL_VERSION',
        'STATE',
        'VIP',
        'IP',
        'PORT',
        'SERVICE_PORTS',
        'UPTIME',
        'IS_VIRTUAL',
        'DOMAIN',
        'SYSTEM_LOCALE',
        'TIME_ZONE',
        'INPUT_LOCALE',
        'AVAILABILITY',
        'RESPONSE_TIME',
        'ERROR_MESSAGE',
        'SOCKET_PATH',
        'LISTEN',
        'AVAILABILITY',
        'RESPONSE_TIME',
        'ERROR_MESSAGE',
        'BELONG_APPLICATION',
        'BELONG_APPLICATION_MODULE',
        'RESOURCE_ID',
        'OS_ID',
        'MACHINE_ID',
        'IP_ADDRS',
        'IPV6_ADDRS',
        'ADMIN_PORT',
        'ADMIN_SSL_PORT',
        'MON_PORT',
        'SSL_PORT',
        'STOMP_PORT',
        'WS_PORT',
        'BRAND',
        'SYS_VENDOR',
        'VENDOR',
        'PRODUCT_UUID',
        'SN',
        'MODEL',
        'UPTIME',
        'OPEN_FILES_COUNT',
        'MAX_OPEN_FILES',
        'OPEN_FILES_INFO',
        'MAX_USER_PROCESS_COUNT',
        'OS_USER',
        'INSTALL_PATH',
        'CONFIG_PATH',
        'EXE_PATH',
        'JVM_TYPE',
        'JVM_VERSION',
        'JAVA_HOME',
        'JAVA_VERSION',
        'JMX_PORT',
        'JMX_SSL',
        'CPU_COUNT',
        'CPU_USAGE',
        'CPU_USAGE_PERCORE',
        'CPU_LOAD_AVG_1',
        'CPU_LOAD_AVG_5',
        'CPU_LOAD_AVG_15',
        'CPU_QUEUE_LEN',
        'CPU_CORES',
        'CPU_LOGIC_CORES',
        'IOWAIT_PCT',
        'CPU_FREQUENCY',
        'CPU_MODE',
        'CPU_MODEL',
        'CPU_BITS',
        'CPU_ARCH',
        'CPU_VERSION',
        'CPU_FIRMWARE_VERSION',
        'CPU_MICROCODE',
        'BIOS_VERSION',
        'TOP_CPU_RPOCESSES',
        'DEFUNC_PROCESSES_COUNT',
        'MIN_HEAP_SIZE',
        'MAX_HEAP_SIZE',
        'MEM_AVAILABLE',
        'MEM_TOTAL',
        'MEM_USAGE',
        'MEM_USED',
        'MEM_FREE',
        'MEM_BUFFERS',
        'MEM_CACHED',
        'SWAP_TOTAL',
        'SWAP_FREE',
        'TOP_MEM_PROCESSES',
        'MOUNT_POINTS',
        'NFS_MOUNTED',
        'DISKS',
        'LISTEN_STATS',
        'CONN_STATS',
        'CONN_OUTBOUND_STATS',
        'DNS_SERVERS',
        'NTP_ENABLE',
        'NTP_SERVERS',
        'NTP_OFFSET_SECS',
        'SSH_VERSION',
        'OPENSSL_VERSION',
        'NIC_BOND',
        'ETH_INTERFACES',
        'USERS',
        'MAIN_ENV'
    ]
    return defaultKeys


def correctTypeDef(fieldDef):
    if 'subset' in fieldDef:
        for subFieldDef in fieldDef['subset']:
            correctTypeDef(subFieldDef)
    else:
        fieldType = fieldDef['type'].lower()
        if fieldType == 'string':
            fieldType = 'String'
        if fieldType == 'text':
            fieldType = 'Text'
        elif fieldType == 'int':
            fieldType = 'Int'
        elif fieldType == 'float':
            fieldType = 'Float'
        elif fieldType == 'jsonarray':
            fieldType = 'JsonArray'
        elif fieldType == 'jsonobject':
            fieldType = 'JsonObject'
        fieldDef['type'] = fieldType

        if 'name' in fieldDef:
            fieldName = fieldDef['name']
            if fieldName == 'AVAILABILITY':
                fieldDef['type'] = 'Int'
            elif fieldName in ('COMMAND', 'HEALTH_CHECK'):
                fieldDef['type'] = 'Text'
            elif fieldType == 'Float' and ('PID' in fieldName or 'PORT' in fieldName):
                fieldDef['type'] = 'Int'
            elif 'IS_' in fieldName:
                fieldDef['type'] = 'Int'
        else:
            if fieldType == 'Float':
                fieldDef['type'] = 'Int'


def getIcon(name):
    nameToIconMap = {
        'a10': 'tsfont-a10',
        'accessendpoint': 'tsfont-accessendpoint',
        'activemq': 'tsfont-activemq',
        'aix': 'tsfont-aix',
        'apache': 'tsfont-apache',
        'db2': 'tsfont-db2',
        'dbins': 'tsfont-db',
        'db': 'tsfont-db',
        'elasticsearch': 'tsfont-elasticsearch',
        'f5': 'tsfont-f5',
        'fcdev': 'tsfont-fcdev',
        'fcswitch': 'tsfont-fcswitch',
        'firewall': 'tsfont-firewall',
        'hadoop': 'tsfont-hadoop',
        'host': 'tsfont-host',
        'iis': 'tsfont-iis',
        'informix': 'tsfont-informix',
        'informix-db': 'tsfont-informix',
        'ins': 'tsfont-instance',
        'java': 'tsfont-java',
        'jboss': 'tsfont-jboss',
        'jetty': 'tsfont-jetty',
        'kafka': 'tsfont-kafka',
        'keepalive': 'tsfont-keepalive',
        'lighttpd': 'tsfont-lighttpd',
        'linux': 'tsfont-linux',
        'memcached': 'tsfont-memcached',
        'mongodb': 'tsfont-mongodb',
        'mssqlserver': 'tsfont-mssqlserver',
        'mssqlserver-db': 'tsfont-mssqlserver',
        'mysql': 'tsfont-mysql',
        'mysql-db': 'tsfont-mysql',
        'netdev': 'tsfont-devices',
        'nginx': 'tsfont-nginx',
        'novmtool': 'tsfont-yunweishenjishebei',
        'oracle-rac': 'tsfont-oracle-rac',
        'oracle': 'tsfont-oracle',
        'oracle-db': 'tsfont-oracle',
        'os': 'tsfont-os',
        'php': 'tsfont-php',
        'postgresql': 'tsfont-postgresql',
        'postgresql-db': 'tsfont-postgresql',
        'python': 'tsfont-python',
        'rabbitmq': 'tsfont-rabbitmq',
        'redis': 'tsfont-redis',
        'resin': 'tsfont-resin',
        'router': 'tsfont-router',
        'secdev': 'tsfont-Webyingyongfanghuxitong',
        'storage': 'tsfont-storages',
        'switch': 'tsfont-switch',
        'sybase': 'tsfont-sybase',
        'sybase-db': 'tsfont-sybase',
        'tomcat': 'tsfont-tomcat',
        'tuxedo': 'tsfont-tuxedo',
        'vcenter': 'tsfont-vcenter',
        'weblogic': 'tsfont-weblogic',
        'websphere': 'tsfont-websphere',
        'windows': 'tsfont-windows',
        'zookeeper': 'tsfont-zookeeper'
    }
    if name in nameToIconMap:
        return nameToIconMap[name]
    else:
        return None


def getDefaultThreshold(dictPath, name, obj_type):
    threshold = []
    jsonFile = dictPath + "/inspect_default_threshold.json"
    if(os.path.isfile(jsonFile)):
        data = {}
        with open(jsonFile, 'r', encoding="utf-8") as f:
            data = json.load(f)
            f.close()
        if name in data:
            threshold = data[name]
        else:
            if obj_type in data:
                threshold = data[obj_type]
    else:
        print("ERROR: Not found json file: inspect_default_threshold.json .")
    return threshold


def checkThresholdExist(threshold, dbThresholds):
    #"$.MOUNT_POINTS[NAME startswith \"/\"].\"USED%\" > 10"
    #"$.MOUNT_POINTS.\"USED%\" >= 90"
    exist = False
    localRule = threshold['rule']
    localKey = ''
    if '[' in localRule:
        localKey = localRule[2:localRule.index('[')]
    else:
        localKey = localRule.split('.')
        localKey = localKey[1]

    for dbThreshold in dbThresholds:
        dbRule = dbThreshold['rule']
        dbKey = ''
        if '[' in dbRule:
            dbKey = dbRule[2:dbRule.index('[')]
        else:
            dbKey = dbRule.split('.')
            dbKey = dbKey[1]

        if localKey == dbKey:
            exist = True
            break
    return exist


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('--lang', default='zh_CN', help='Language')
    parser.add_argument('--action', default='savetodb', help='Actions:（savetodb|append|remove|beautify|dropcollection）')
    parser.add_argument('--filter', default='{}', help='Match filter')
    parser.add_argument('--content', default='', help='Append Json')

    args = parser.parse_args()
    lang = args.lang
    action = args.action

    binPaths = os.path.split(os.path.realpath(__file__))
    dictPath = os.path.abspath(binPaths[0])

    dirs = os.listdir(dictPath)

    dbclient = None
    collection = None
    inspectDef = None
    if action == 'savetodb':
        import AutoExecUtils
        (dbclient, db) = AutoExecUtils.getDB()
        table = '_dictionary'
        collection = db[table]
        inspectDef = db['_inspectdef']

    for dfile in dirs:
        jsonFile = dictPath + "/" + dfile

        info = dfile.split('.')
        if (len(info) == 3 and info[1] == lang):
            name = info[0]
            hasError = False
            data = {}

            if action not in ('dropcollection', 'clearcollection'):
                print("INFO: Try to load json file:", dfile, "...")
                with open(jsonFile, 'r', encoding="utf-8") as f:
                    data = json.load(f)
                    f.close()

                if 'collection' not in data:
                    hasError = True
                    print("ERROR: Object decription data does not defined attribue 'collection'.\n")
                if 'filter' not in data:
                    hasError = True
                    print("ERROR: Object decription data does not defined attribue 'filter'.\n")
                if 'label' not in data:
                    hasError = True
                    print("ERROR: Object decription data does not defined attribue 'label'.\n")

            if action == 'savetodb':
                print("INFO: Import", dfile, "start...")
                if hasError == False:
                    tableName = data['collection']
                    collection.delete_one({'name': name})

                    data['name'] = name
                    collection.replace_one({'name': name}, data, upsert=True)
                    collection.create_index([('name', 1)], name='idx_pk', unique=True)
                    print("INFO: Import", dfile, "success.")

                    # 巡检文件内定义的默认阀值
                    defaultThreshold = getDefaultThreshold(dictPath, data['label'], tableName)
                    print("INFO: {} inspect plugin default threshold Loaded." .format(name))
                    # 获取已经选择进入巡检报告定义的字段
                    inspectObj = inspectDef.find_one({'name': name})
                    # 如果巡检定义不存在则自动生成一个空的定义
                    if inspectObj is None:
                        inspectObj = {'name': name,
                                      'label': data['label'],
                                      'icon': getIcon(name),
                                      'collection': data['collection'],
                                      'filter': data['filter'],
                                      'fields': [],
                                      'thresholds': defaultThreshold}
                    else:
                        inspectObj['icon'] = getIcon(name)
                        inspectObj['label'] = data['label']
                        inspectObj['collection'] = data['collection']
                        inspectObj['filter'] = data['filter']
                        # 合并已存在阀值和文件定义默认阀值
                        dbThreshold = inspectObj['thresholds']
                        if len(dbThreshold) == 0:
                            inspectObj['thresholds'] = defaultThreshold
                        else:
                            newThreshold = dbThreshold.copy()
                            for threshold in defaultThreshold:
                                if checkThresholdExist(threshold, dbThreshold) == False:
                                    newThreshold.append(threshold)
                            inspectObj['thresholds'] = newThreshold

                    # 获取已经定义的fields
                    inspectFields = []
                    inspectFieldsMap = {}
                    inspectFields = inspectObj['fields']
                    for fieldObj in inspectFields:
                        inspectFieldsMap[fieldObj['name']] = 1

                    # 如果集合定义中存在新的字段不在巡检定义里，则自动加入巡检定义中
                    for collectFieldObj in data['fields']:
                        if collectFieldObj['name'] not in inspectFieldsMap:
                            inspectFields.append({'name': collectFieldObj['name'],
                                                  'selected': 1})

                    inspectDef.replace_one({'name': name}, inspectObj, upsert=True)
                    inspectDef.create_index([('name', 1)], name='idx_pk', unique=True)
                    print("INFO: Update inspect define for ", dfile, "success.")
                else:
                    print("ERROR: Import", dfile, "failed.")

            elif action == 'remove':
                matchFilter = args.filter
                filterObj = json.loads(matchFilter)
                filterMatched = True
                for filterKey in filterObj.keys():
                    if filterKey not in data or data[filterKey] != filterObj[filterKey]:
                        print("INFO: " + data[filterKey] + " not match filter " + matchFilter)
                        filterMatched = False
                        break
                if not filterMatched:
                    continue

                removeKey = args.content
                newFields = []
                fields = data['fields']
                for field in fields:
                    if 'name' in field and field['name'] == removeKey:
                        print("INFO: Remove field " + removeKey + " from " + dfile)
                        continue
                    else:
                        newFields.append(field)
                data['fields'] = newFields
                with open(jsonFile, 'w', encoding="utf-8") as f:
                    json.dump(data, f, indent=4, ensure_ascii=False)
                    f.close()

            elif action == 'append':
                content = args.content
                if content != '':
                    print("INFO: Append entry to ", dfile, "start...")

                    matchFilter = args.filter
                    filterObj = json.loads(matchFilter)
                    filterMatched = True
                    for filterKey in filterObj.keys():
                        if filterKey not in data or data[filterKey] != filterObj[filterKey]:
                            print("INFO: " + data[filterKey] + " not match filter " + matchFilter)
                            filterMatched = False
                            break
                    if not filterMatched:
                        continue

                    jsonObj = json.loads(content)
                    if 'name' not in jsonObj:
                        print("ERROR: Attribute name not in json:", content)
                    if 'desc' not in jsonObj:
                        print("ERROR: Attribute desc not in json:", content)
                    if 'type' not in jsonObj:
                        print("ERROR: Attribute desc not in json:", content)
                    else:
                        typeVal = jsonObj['type']
                        if typeVal not in ('String', 'JsonArray', 'JsonObject', 'Int', 'Float'):
                            print("ERROR: Invalid attribute type:" + typeVal, ', only support String|JsonArray|JsonObject|Int|Float')

                    data['fields'].append(jsonObj)
                    with open(jsonFile, 'w', encoding="utf-8") as f:
                        json.dump(data, f, indent=4, ensure_ascii=False)
                        f.close()

            elif action == 'beautify':
                print("INFO: Beautify ", dfile, "start...")
                fields = data['fields']

                fieldsMap = {}
                for field in fields:
                    fieldName = field['name']
                    fieldsMap[fieldName] = field

                newFields = []
                for defaultKey in getDefaultFieldKeys():
                    if defaultKey in fieldsMap:
                        correctTypeDef(fieldsMap[defaultKey])
                        newFields.append(fieldsMap[defaultKey])
                        del(fieldsMap[defaultKey])

                leftKeys = sorted(fieldsMap.keys())
                for key in leftKeys:
                    correctTypeDef(fieldsMap[key])
                    newFields.append(fieldsMap[key])

                data['fields'] = newFields
                with open(jsonFile, 'w', encoding="utf-8") as f:
                    json.dump(data, f, indent=4, ensure_ascii=False)
                    f.close()
                    print("INFO: Beautify ", dfile, "success.")
            elif action == 'dropcollection':
                import AutoExecUtils
                (dbclient, db) = AutoExecUtils.getDB()
                colName = args.content
                if colName == 'all':
                    for colName in ('ACCESSENDPOINT', 'APPLICATION', 'CLUSTER', 'DB', 'DBINS', 'EMPTY', 'FCDEV', 'HOST', 'INS', 'K8S', 'LOADBALANCER', 'NETDEV', 'OS', 'SECDEV', 'STORAGE', 'UNKNOWN', 'VIRTUALIZED'):
                        col = db[colName]
                        col.drop()
                else:
                    col = db[colName]
                    col.drop()
            elif action == 'clearcollection':
                import AutoExecUtils
                (dbclient, db) = AutoExecUtils.getDB()
                colName = args.content
                if colName == 'all':
                    for colName in ('ACCESSENDPOINT', 'APPLICATION', 'CLUSTER', 'DB', 'DBINS', 'EMPTY', 'FCDEV', 'HOST', 'INS', 'K8S', 'LOADBALANCER', 'NETDEV', 'OS', 'SECDEV', 'STORAGE', 'UNKNOWN', 'VIRTUALIZED'):
                        col = db[colName]
                        col.delete_many({})
                else:
                    col = db[colName]
                    col.delete_many({})
            else:
                print("ERROR: Action " + action + "not supported.")
    # 关闭连接
    if dbclient is not None:
        dbclient.close()
